package com.ae.shield.common.recipe.assembly;

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.ae.shield.ModLib;
import com.ae.shield.common.enchantment.EnchantmentWrap;
import com.ae.shield.common.enchantment.shieldSuffix.SuffixBase;
import com.ae.shield.common.enchantment.shieldSuffix.SuffixFactory;
import com.ae.shield.common.fluid.FluidHelper;
import com.ae.shield.common.items.ItemList;
import com.ae.shield.common.items.shieldFittings.SuffixItem;
import com.ae.shield.common.recipe.ModRecipesType;
import com.ae.shield.common.tag.TagHelper;
import net.minecraft.enchantment.Enchantment;
import net.minecraft.fluid.Fluid;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.IRecipeSerializer;
import net.minecraft.item.crafting.IRecipeType;
import net.minecraft.item.crafting.Ingredient;
import net.minecraft.network.PacketBuffer;
import net.minecraft.tags.FluidTags;
import net.minecraft.tags.ITag;
import net.minecraft.util.JSONUtils;
import net.minecraft.util.NonNullList;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.registries.ForgeRegistryEntry;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;

public class SuffixUpgradeAssemblyRecipe extends AssemblyRecipe {

    private final EnchantmentWrap[] suffixes;

    public SuffixUpgradeAssemblyRecipe(ResourceLocation id, EnchantmentWrap suffixIn, EnchantmentWrap suffixOut, Fluid fluid, ITag<Fluid> fluidTag, int fluidCount, int energy, int tick, Ingredient... itemInputs) {
        super(id, new ItemStack(ItemList.SUFFIX_KID.get()), fluid, fluidTag, fluidCount, energy, tick, itemInputs);
        suffixes = new EnchantmentWrap[]{suffixIn, suffixOut};
    }

    public boolean matches(FluidStack fluid, NonNullList<ItemStack> stacks) {
        ItemStack stack0 = stacks.get(0);
        if (stack0.getItem() instanceof SuffixItem) {
            SuffixBase suffix = (SuffixBase) suffixes[0].getEn();
            int level = suffixes[0].getLvl();
            ArrayList<EnchantmentWrap> ens = ((SuffixItem) stack0.getItem()).readSuffix(stack0);
            boolean haveEn = false;

            if (ens != null && !ens.isEmpty()) {
                for (EnchantmentWrap en : ens) {
                    if (en.getEn() == suffix && en.getLvl() == level) {
                        haveEn = true;
                        break;
                    }
                }

                if (haveEn) {
                    return super.matches(fluid, stacks);
                }
            }
        }

        return false;
    }

    @Nonnull
    @Override
    public ItemStack getRecipeOutput() {
        Item item;
        int lvl = suffixes[1].getLvl();

        if (lvl > 2) {
            item = ItemList.INTENSIFIED_SUFFIX_KID.get();
        } else {
            item = ItemList.SUFFIX_KID.get();
        }

        ItemStack stack = new ItemStack(item);
        ArrayList<EnchantmentWrap> en = new ArrayList<>();
        en.add(suffixes[1]);
        ((SuffixItem) stack.getItem()).writeSuffix(en, stack);
        return stack;
    }

    public static ResourceLocation getTypeId() {
        return new ResourceLocation(ModLib.MOD_ID, "suffix_upgrade_assembly");
    }

    @Nonnull
    @Override
    public IRecipeSerializer<?> getSerializer() {
        return ModRecipesType.SUFFIX_UPGRADE_ASSEMBLY_SERIALIZER.get();
    }

    public static class SuffixUpgradeAssemblyRecipeType implements IRecipeType<SuffixUpgradeAssemblyRecipe> {
        @Override
        public String toString() {
            return "suffix_upgrade_assembly";
        }
    }

    public static class Serializer extends ForgeRegistryEntry<IRecipeSerializer<?>> implements IRecipeSerializer<SuffixUpgradeAssemblyRecipe> {

        @Nonnull
        @Override
        public SuffixUpgradeAssemblyRecipe read(@Nonnull ResourceLocation id, @Nonnull JsonObject json) {
            Fluid fluid = null;
            ITag<Fluid> fluidTag = null;
            int fluidCount = 0;

            JsonObject suffix = JSONUtils.getJsonObject(json, "suffix");
            EnchantmentWrap suffixOut = SuffixFactory.deserializeSuffix(JSONUtils.getJsonObject(suffix, "suffix_out"));
            EnchantmentWrap suffixIn = SuffixFactory.deserializeSuffix(JSONUtils.getJsonObject(suffix, "suffix_in"));
            if (JSONUtils.hasField(json, "fluid_ingredient")) {
                JsonObject fluidJson = JSONUtils.getJsonObject(json, "fluid_ingredient");
                fluid = FluidHelper.deserializeFluid(fluidJson);

                if (fluid == null) {
                    fluidTag = TagHelper.deserializeFluidTag(fluidJson);
                }

                fluidCount = JSONUtils.getInt(fluidJson, "count");
            }

            int energy = JSONUtils.getInt(json, "energy_per_tick");
            int tick = JSONUtils.getInt(json, "processing_time");

            JsonArray ingredients = JSONUtils.getJsonArray(json, "ingredients");
            Ingredient backing = Ingredient.deserialize(JSONUtils.getJsonArray(json, "backing"));
            ItemStack stack = backing.getMatchingStacks()[0];
            ArrayList<EnchantmentWrap> ens = new ArrayList<>();
            ens.add(suffixIn);
            ((SuffixItem) stack.getItem()).writeSuffix(ens, stack);
            List<Ingredient> inputs = new ArrayList<>();
            inputs.add(Ingredient.fromStacks(stack));

            for (JsonElement e : ingredients) {
                inputs.add(Ingredient.deserialize(e));
            }

            return new SuffixUpgradeAssemblyRecipe(id, suffixIn, suffixOut, fluid, fluidTag, fluidCount, energy, tick, inputs.toArray(new Ingredient[0]));
        }

        @Nullable
        @Override
        public SuffixUpgradeAssemblyRecipe read(@Nonnull ResourceLocation id, @Nonnull PacketBuffer buffer) {
            SuffixBase suffixIn = (SuffixBase) ForgeRegistries.ENCHANTMENTS.getValue(buffer.readResourceLocation());
            EnchantmentWrap enIn = EnchantmentWrap.wrap(suffixIn, buffer.readInt());
            SuffixBase suffixOut = (SuffixBase) ForgeRegistries.ENCHANTMENTS.getValue(buffer.readResourceLocation());
            EnchantmentWrap enOut = EnchantmentWrap.wrap(suffixOut, buffer.readInt());

            int fluidFlag = buffer.readInt();
            Fluid fluid = null;
            ITag<Fluid> fluidTag = null;

            switch (fluidFlag) {
                case 1:
                    FluidStack fluidStack = buffer.readFluidStack();
                    fluid = fluidStack.getFluid();
                    break;
                case 2:
                    fluidTag = FluidTags.createOptional(buffer.readResourceLocation());
                    break;
            }

            int[] ints = buffer.readVarIntArray(3);

            Ingredient[] inputs = new Ingredient[buffer.readVarInt()];
            for (int i = 0; i < inputs.length; i++) {
                inputs[i] = Ingredient.read(buffer);
            }
            return new SuffixUpgradeAssemblyRecipe(id, enIn, enOut, fluid, fluidTag, ints[0], ints[1], ints[2], inputs);
        }

        @Override
        public void write(PacketBuffer buffer, SuffixUpgradeAssemblyRecipe recipe) {
            Enchantment enIn = recipe.suffixes[0].getEn();
            buffer.writeResourceLocation(enIn.getRegistryName());
            buffer.writeInt(recipe.suffixes[0].getLvl());
            Enchantment enOut = recipe.suffixes[1].getEn();
            buffer.writeResourceLocation(enOut.getRegistryName());
            buffer.writeInt(recipe.suffixes[1].getLvl());

            if (recipe.fluid != null) {
                buffer.writeInt(1);
                buffer.writeFluidStack(new FluidStack(recipe.fluid, 1));
            } else if (recipe.fluidTag != null) {
                buffer.writeInt(2);
                buffer.writeResourceLocation(((ITag.INamedTag<Fluid>) recipe.fluidTag).getName());
            } else {
                buffer.writeInt(0);
            }

            buffer.writeVarIntArray(new int[]{recipe.fluidCount, recipe.energy, recipe.tick});

            buffer.writeVarInt(recipe.itemInputs.size());
            for (Ingredient input : recipe.getIngredients()) {
                input.write(buffer);
            }
        }
    }
}
